package com.swotxu.screwweb.service;

import cn.smallbun.screw.core.engine.EngineFileType;
import com.swotxu.screwweb.config.ScrewConfiguration;
import com.swotxu.screwweb.config.ScrewProperties;
import com.swotxu.screwweb.util.IOUtil;
import com.swotxu.screwweb.util.ReentrantReadWriteLockUtil;
import com.swotxu.screwweb.util.ScrewUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

/**
 * @Date: 2021/7/15 11:01
 * @Author: xu.hai
 */
@Service
public class ScrewService {

    private static final Logger log = LoggerFactory.getLogger(ScrewService.class);

    @Autowired
    ReentrantReadWriteLockUtil lockUtil;
    @Autowired
    private ScrewConfiguration configuration;
    @Autowired
    private AsyncService asyncService;

    /**
     * 处理文件请求
     * @param response  响应消息实体
     * @param dbKey     db配置的id
     * @param fileType  文件类型（HTML等）
     * @param isView    是否在线查看 true-在线 false-下载
     */
    public void docStreamProcess(HttpServletResponse response, String dbKey, EngineFileType fileType, boolean isView) {
        Map<String, ScrewProperties.ScrewDBInfo> dbInfoMap = configuration.getScrewProperties().toIdMap();
        if (!dbInfoMap.containsKey(dbKey)) {
            IOUtil.context2OutWeb(response, "未找到指定key: " + dbKey);
            return;
        }
        ScrewProperties.ScrewDBInfo dbInfo = dbInfoMap.get(dbKey);
        File docPath = dbInfo.getDocPath(fileType);

        // 文件不存在，获取写锁再生成文件
        if (!docPath.exists()) {
            log.info(">> 文件不存在，开始尝试获取写锁! {}", docPath);
            ReentrantReadWriteLock.WriteLock writeLock = lockUtil.getWriteLock(dbKey);
            try {
                writeLock.lock();
                if (!docPath.exists()) {
                    log.info("写锁获取成功，开始写入文件! {}", docPath);
                    ScrewUtil.create(dbInfo, fileType);
                }
            } finally {
                writeLock.unlock();
            }
        }
        // 获取读锁再读文件
        ReentrantReadWriteLock.ReadLock readLock = lockUtil.getReadLock(dbKey);
        try {
            readLock.lock();
            log.info(">> 获取读锁成功，开始读取文件! {}", docPath);
            IOUtil.downloadByPath(response, docPath, isView);
        } finally {
            readLock.unlock();
        }
    }

    /**
     * 重新加载配置文件
     */
    public void reloadFile() {
        // 旧的db配置
        Map<String, ScrewProperties.ScrewDBInfo> oldDBInfo = configuration.getScrewProperties().toSignatureMap();
        // 重新加载配置
        boolean reload = configuration.reload();
        if (reload) {
            // 新的db配置
            Map<String, ScrewProperties.ScrewDBInfo> newDBInfo = configuration.getScrewProperties().toSignatureMap();
            // 检索出未变配置，异步更新
            List<ScrewProperties.ScrewDBInfo> infoList = unDiffDBInfo(oldDBInfo, newDBInfo);
            infoList.forEach(asyncService::asyncOverrideFile);
        }
    }

    /**
     * 比较新旧配置，检索出未变更的配置（用于异步更新覆盖）
     * @param oldDBInfo 旧配置
     * @param newDBInfo 新配置
     * @return 未变配置
     */
    private List<ScrewProperties.ScrewDBInfo> unDiffDBInfo(Map<String, ScrewProperties.ScrewDBInfo> oldDBInfo, Map<String, ScrewProperties.ScrewDBInfo> newDBInfo) {
        return oldDBInfo.keySet().stream().filter(newDBInfo::containsKey).map(newDBInfo::get).collect(Collectors.toList());
    }
}
